package org.light.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.light.core.Writeable;
import org.light.utils.StringUtil;
import org.light.utils.WriteableUtil;

public class Method implements Comparable,Serializable{
	private static final long serialVersionUID = -1434545681196512706L;
	protected Long serial = 0L;
	protected int indent = 0;
	protected List<Signature> signatures = new ArrayList<Signature>();
	protected String standardName;
	protected String methodToken;
	protected String content = "";
	protected String methodComment;
	protected Type returnType;
	protected String returnTypePackageToken;
	protected StatementList methodStatementList = new StatementList();
	protected boolean throwException;
	protected List<String> otherExceptions = new ArrayList<String>();
	protected boolean isprotected = false;
	protected boolean noContainer = false;
	protected Set<String> additionalImports = new TreeSet<String>(new StringComparator());
	protected Set<String> metaDatas = new TreeSet<String>(new StringComparator());
	protected String tempTag = "";
	protected boolean isSync = false;
	protected boolean isStatic = false;

	public Long getSerial() {
		return serial;
	}

	public void setSerial(Long serial) {
		this.serial = serial;
	}

	public int getIndent() {
		return indent;
	}

	public void setIndent(int indent) {
		this.indent = indent;
	}

	public boolean isNoContainer() {
		return noContainer;
	}

	public void setNoContainer(boolean noContainer) {
		this.noContainer = noContainer;
	}
	
	public Type getReturnType() {
		return returnType;
	}

	public void setReturnType(Type returnType) {
		this.returnType = returnType;
	}

	public List<Signature> getSignatures() {
		return signatures;
	}

	public void setSignatures(List<Signature> signatures) {
		this.signatures = signatures;
	}

	public String getMethodComment() {
		return methodComment;
	}

	public String getMethodToken() {
		return methodToken;
	}

	public void setMethodToken(String methodToken) {
		this.methodToken = methodToken;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public void setMethodComment(String methodComment) {
		this.methodComment = methodComment;
	}

	public String getStandardName() {
		return standardName;
	}

	public void setStandardName(String standardName) {
		this.standardName = standardName;
	}
	
	public String getCapFirstMethodName() {
		return StringUtil.capFirst(standardName);
	}
	
	public String getLowerFirstMethodName() {
		return StringUtil.lowerFirst(standardName);
	}

	public void addSignature(Signature signature) {
		this.signatures.add(signature);
	}

	public String generateMethodString() {
		if (!noContainer){
			Type returnType = this.returnType;
			StringBuilder sb = new StringBuilder();
			if (returnType == null ||  "".equals(returnType)) returnType = new Type("void");
			for (String metaData : this.getMetaDatas()){
				sb.append("\t@").append(metaData).append("\n");
			}
			if (!this.isIsprotected()) sb.append("\tpublic ");
			else sb.append("\tprotected ");
			if (this.isStatic()) sb.append("static ");
			sb.append(returnType).append(" ").append(this.standardName).append("(");
			Iterator it = this.getSignatures().iterator();
			boolean hasSig = false;
			while (it.hasNext()) {
				hasSig = true;
				Signature signature = (Signature) it.next();
				if (hasSig&&this.getSignatures().size()>5&&this.metaDatas!=null){
					if (signature.position == 1)
						sb.append(signature.generateCallString()).append(",\n");
					else if  (signature.position > 1 && signature.position < this.getSignatures().size())
						sb.append("\t\t"+signature.generateCallString()).append(",\n");
					else if ( signature.position == this.getSignatures().size())
						sb.append("\t\t"+signature.generateCallString()).append(",");
				}else {
					sb.append(signature.generateCallString()).append(",");
				}
			}
			if (hasSig) {
				sb = new StringBuilder(sb.substring(0, sb.length() - 1));
			}
			sb.append(")");
			if (this.throwException && this.otherExceptions.size() == 0) sb.append(" throws Exception");
			else if (this.throwException && this.otherExceptions.size() > 0) {
				sb.append(" throws ");
				for (String exception : this.otherExceptions){
					sb.append(exception).append(",");
				}
				if (sb.length()>1 && sb.charAt(sb.length()-1)=='\n')
					sb = new StringBuilder(sb.substring(0, sb.length() - 2));
				else
					sb = new StringBuilder(sb.substring(0, sb.length() - 1));
			}
			sb.append("{\n");
			if (this.content != null && !"".equals(content)){
				sb.append(content);
			} else if (this.getMethodStatementList() != null && this.getMethodStatementList().size() > 0){
				Collections.sort(this.methodStatementList);
				for (Statement s: this.methodStatementList){
					sb.append(s.getContent()).append("\n");
				}
			}
			sb.append("\t}\n");
			return sb.toString();
		}else{
			StringBuilder sb = new StringBuilder();
			if (this.content != null && !"".equals(content)){
				sb.append(content);
			} else if (this.getMethodStatementList() != null && this.getMethodStatementList().size() > 0){
				Collections.sort(this.methodStatementList);
				for (Statement s: this.methodStatementList){
					sb.append(s.getContent()).append("\n");
				}
			}
			return sb.toString();
		}		
	}
	
	public Set<String> getMetaDatas() {
		return metaDatas;
	}

	public void setMetaDatas(Set<String> metaDatas) {
		this.metaDatas = metaDatas;
	}
	
	public void addMetaData(String metaData){
		this.metaDatas.add(metaData);
	}

	public String generateMethodDefinition(){
		StringBuilder sb = new StringBuilder("");
		for (String annon:this.getMetaDatas()) {
			sb.append("\t@").append(annon).append("\n");
		}
		sb.append("\tpublic ").append(this.returnType)
				.append(" ").append(this.standardName).append("(");
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			if (hasSig&&this.getSignatures().size()>5&&this.metaDatas!=null){
				if (signature.position == 1)
					sb.append(signature.generateCallString()).append(",\n");
				else if  (signature.position > 1 && signature.position < this.getSignatures().size())
					sb.append("\t\t"+signature.generateCallString()).append(",\n");
				else if ( signature.position == this.getSignatures().size())
					sb.append("\t\t"+signature.generateCallString()).append(",");
			}else {
				sb.append(signature.generateCallString()).append(",");
			}
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		if (this.throwException && this.otherExceptions.size() == 0) sb.append(" throws Exception");
		else if (this.throwException && this.otherExceptions.size() > 0) {
			sb.append(" throws ");
			for (String exception : this.otherExceptions){
				sb.append(exception).append(",");
			}
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(";");
		return sb.toString();
	}	

	public String getReturnTypePackageToken() {
		return returnTypePackageToken;
	}

	public void setReturnTypePackageToken(String returnTypePackageToken) {
		this.returnTypePackageToken = returnTypePackageToken;
	}

	public String getThrowException() {
		return "Exception";
	}

	public void setThrowException(boolean throwException) {
		this.throwException = throwException;
	}
	
	public boolean isThrowException(){
		return this.throwException;
	}

	public StatementList getMethodStatementList() {
		this.methodStatementList.setSerial(this.serial);
		this.methodStatementList.setIndent(this.indent);
		return this.methodStatementList;
	}

	public void setMethodStatementList(StatementList methodStatementList){		
		this.methodStatementList = methodStatementList;
	}
	
	public String generateMethodContentStringWithSerial() {
		if (this.methodStatementList != null){
			StringBuilder sb = new StringBuilder();
			Collections.sort(this.methodStatementList);
			for (Statement s : this.methodStatementList){
				sb.append("\t\t"+s.getSerial()+"\t:\t"+s.getContent()+"\n");
			}
			return sb.toString();
		}
		else return this.content;
	}
	
	public String generateStandardServiceImplCallString(String daoString){
		StringBuilder sb = new StringBuilder(daoString + "." + this.getStandardName()).append("("); 
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			String name = signature.getName();			
			sb.append(name).append(",");
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		return sb.toString();
	}
	
	public String generateStandardServiceImplUsingDomainPrefixCallString(String daoString,Domain domain){
		StringBuilder sb = new StringBuilder(daoString + "." + this.getStandardName()).append("("); 
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			String name = domain.getLowerFirstDomainName()+".get"+StringUtil.capFirst(signature.getName())+"()";			
			sb.append(name).append(",");
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		return sb.toString();
	}
	
	public String generateStandardDaoImplCallString(String daoString){
		StringBuilder sb = new StringBuilder(daoString + "." + this.getStandardName()).append("("); 
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			String name = signature.getName();			
			sb.append(name).append(",");
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		return sb.toString();
	}
	
	public String generateStandardCallString(){
		StringBuilder sb = new StringBuilder( this.getStandardName()).append("("); 
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			String name = signature.getName();			
			sb.append(name).append(",");
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		return sb.toString();
	}
	
	public String generateStandardServiceImplCallString(){
		StringBuilder sb = new StringBuilder( this.getStandardName()).append("("); 
		Iterator it = this.getSignatures().iterator();
		boolean hasSig = false;
		while (it.hasNext()) {
			hasSig = true;
			Signature signature = (Signature) it.next();
			String name = signature.getName();			
			sb.append(name).append(",");
		}
		if (hasSig) {
			sb = new StringBuilder(sb.substring(0, sb.length() - 1));
		}
		sb.append(")");
		return sb.toString();
	}

	public List<String> getOtherExceptions() {
		return otherExceptions;
	}

	public void setOtherExceptions(List<String> otherExceptions) {
		this.otherExceptions = otherExceptions;
	}

	public boolean isIsprotected() {
		return isprotected;
	}

	public void setIsprotected(boolean isprotected) {
		this.isprotected = isprotected;
	}

	public Set<String> getAdditionalImports() {
		return additionalImports;
	}

	public void setAdditionalImports(Set<String> additionalImports) {
		this.additionalImports = additionalImports;
	}
	
	public void addAdditionalImport(String additionalImport) {
		this.additionalImports.add(additionalImport);
	}
	
	public String getStandardCallString(){
		boolean hasSignature = false;
		StringBuilder sb = new StringBuilder("");
		sb.append(this.getStandardName()).append("(");
		for (Signature s :this.signatures){
			sb.append(s.getName()).append(",");
			if (hasSignature == false) hasSignature = true;
		}
		sb.deleteCharAt(sb.length()-1); 
		sb.append(")");
		return sb.toString();
	}
	
	@Override
	public int compareTo(Object o) {
		int result = this.standardName.compareTo(((Method)o).getStandardName());
		if (result == 0){
			int result2 = ((Integer)this.signatures.size()).compareTo((Integer)((Method)o).getSignatures().size());
			if (result2 != 0) return result2;
			else {
				for (int i=0;i<this.signatures.size();i++){
					int result3 = this.signatures.get(i).compareTo(((Method)o).getSignatures().get(i));
					if (result3 != 0) {
						return result3;
					}
				}
				return 0;
			}
		}else{
			return result;
		}
	}
	
	@Override
	public boolean equals(Object o){
		return this.getStandardName().equals(((Method)o).getStandardName());
	}

	public String getTempTag() {
		return tempTag;
	}

	public void setTempTag(String tempTag) {
		this.tempTag = tempTag;
	}

	public boolean isSync() {
		return isSync;
	}

	public void setSync(boolean isSync) {
		this.isSync = isSync;
	}
	
	public StatementList generateMethodFullStatements() {
		if (!noContainer){
			List<Writeable> sList = new ArrayList<Writeable>();
			long serial = 1000L;
			Type returnType = this.returnType;
			
			if (returnType == null ||  "".equals(returnType)) returnType = new Type("void");
			for (String metaData : this.getMetaDatas()){
				sList.add(new Statement(serial,1,"@"+metaData));
				serial += 1000L;
			}
			StringBuilder sb = new StringBuilder();
			if (!this.isIsprotected()) sb.append("public ");
			else sb.append("protected ");
			if (this.isStatic()) sb.append("static ");
			sb.append(returnType).append(" ").append(this.standardName).append("(");
			Iterator it = this.getSignatures().iterator();
			boolean hasSig = false;
			while (it.hasNext()) {
				hasSig = true;
				Signature signature = (Signature) it.next();
				if (hasSig&&this.getSignatures().size()>5&&this.metaDatas!=null){
					if (signature.position == 1)
						sb.append(signature.generateCallString()).append(", ");
					else if  (signature.position > 1 && signature.position < this.getSignatures().size())
						sb.append(signature.generateCallString()).append(", ");
					else if ( signature.position == this.getSignatures().size())
						sb.append(signature.generateCallString()).append(", ");
				}else {
					sb.append(signature.generateCallString()).append(", ");
				}
			}
			if (hasSig) {
				sb = new StringBuilder(sb.substring(0, sb.length() - 2));
			}
			sb.append(")");
			if (this.throwException && this.otherExceptions.size() == 0) sb.append(" throws Exception");
			else if (this.throwException && this.otherExceptions.size() > 0) {
				sb.append(" throws ");
				for (String exception : this.otherExceptions){
					sb.append(exception).append(",");
				}
				if (sb.length()>1 && sb.charAt(sb.length()-1)=='\n')
					sb = new StringBuilder(sb.substring(0, sb.length() - 2));
				else
					sb = new StringBuilder(sb.substring(0, sb.length() - 1));
			}
			sb.append("{");
			sList.add(new Statement(serial,1,sb.toString()));			
			StatementList sl = this.getMethodStatementList();
			sl.setSerial(serial+1000L);
			sList.add(sl);
			sList.add(new Statement(serial+2000L,1,"}"));
			return WriteableUtil.merge(sList);
		}else{
			return this.getMethodStatementList();
		}		
	}
	
	public StatementList generateMethodFullStatements(long seril,int indent) {
		StatementList sl = generateMethodFullStatements();
		sl.setSerial(seril);
		sl.setIndent(indent);
		return sl;
	}

	public boolean isStatic() {
		return isStatic;
	}

	public void setStatic(boolean isStatic) {
		this.isStatic = isStatic;
	}
}
